Reducció de fitxers de configuració

La reducció de fitxers de configuració al framework Canigó s'ha realitzat incorporant dues tecnologies diferenciades que ofereixen la reducció de fitxers a diferents apartats de la configuració del framework. Una tecnologia és la que ofereix Spring 2.0 que permet reduïr els fitxers necessaris per a aconseguir els mateixos efectes que s'aconseguien en les versions < 2.0. Una altra tecnologia és la que ofereix SiteMesh, un framework per gestionar el disseny de les aplicacions web. Tot seguit es concreten les possibilitats i l'ús de la tecnologia spring.

Spring 2.0

Spring 2.0 té una nova capacitat anomenada "extensible XML configuration" a partir de la qual es dóna suport al desenvolupament d'elements fets a mida. Aquests elements fets a mida ofereixen un nou nivell d'abstracció per generar definicions de beans de Spring. El mecanisme d'extensió de XML també proveeix de noves etiquetes per simplificar les tàsques més habituals.

Noves etiquetes per a fer les tasques més habituals

Per a poder utilitzar les noves etiquetes és necessari utilitzar els XML Schema en comptes dels DTD, i importar el namespace desitjat. Tanmateix aquestes etiquetes es poden mesclar amb les definicions tradicionals de beans.

Exemple de codi que s'ha d'incloure per a usar el namespace tx

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

Les etiquetes que ofereix Spring 2.0 són:
Transaction management ("tx"), AOP ("aop"), Java EE ("jee"), Dynamic languages ("lang") i Utils ("util"). Per a més informació cal consultar la documentació de Spring .
Així doncs a tall d'exemple aquí es comparen dues tasques en les seves maneres de fer amb Spring 2.0 i amb Spring < 2.0.
 Definició del datasource amb spring < 2.0
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
     <property name="jndiName" value="jdbc/jpetsore" />
</bean>

Definició del datasource amb spring 2.0
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/jpetstore"/>

Definició del caràcter transaccional pels beans amb spring < 2.0

<bean class="org.springframework...DefaultAdvisorAutoProxyCreator"/>
  <bean class="org.springframework...TransactionAttributeSourceAdvisor">
     <property name="transactionInterceptor ref="transactionInterceptor"/>
  </bean>

  <bean id="transactionInterceptor" class="org.springframework...TransactionInterceptor">
     <property name="transactionManager" ref="transactionManager"/>
     <property name="transactionAttributeSource">
         <bean class="org.springframework...AnnotationsTransactionAttributeSource">
 	 </bean>
     </property>
 </bean>

Definició del caràcter transaccional pels beans amb spring 2.0

<tx:annotation-driven />
Implementació d'una extensió XML

La implementació de namespaces és relativament fàcil. És un procés compost de tres passos. En l'exemple que s'ha posat, el 2n pas està descomposat en dos passos per simplificar la feina.

1. Definir un schema XML. No ha de complir cap restricció especial. El fitxer schema es pot posar allà on es cregui més convenient, sempre i quan després es configuri correctament la seva localització.

Exemple de schema

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.springframework.org/schema/myns"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:beans="http://www.springframework.org/schema/beans"
    targetNamespace="http://www.mycompany.com/schema/myns"
    elementFormDefault="qualified"
    attributeFormDefault="unqualified">
   <xsd:import namespace="http://www.springframework.org/schema/beans"/>
   <xsd:element name="dateformat">
      <xsd:complexType>
         <xsd:complexContent>
            <xsd:extension base="beans:identifiedType">
               <xsd:attribute name="lenient" type="xsd:boolean"/>
               <xsd:attribute name="pattern" type="xsd:string" use="required"/>
            </xsd:extension>
         </xsd:complexContent>
      </xsd:complexType>
   </xsd:element>
</xsd:schema>

2. Generar una classe que implementi la interfície NamespaceHandler. Aquesta classe ha de generar objectes BeanDefinition amb els elements i atributs del schema definit al punt 1.

La interfície NamespaceHandler agafa els elements W3C DOM i en genera metadades BeanDefinition processant-los. Spring ja s'encarrega de fer el parseig del XML. Els mètodes de la implementació de la interfídie NamespaceHandler només han de recòrrer l'abre resultant del parseig.

Interfície NamespaceHandler

public interface NamespaceHandler {
     void init();
     BeanDefinition parse(Element element, ParserContext parserContext);

      BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext);
  }

El mètode parse() és el més important, té la funcionalitat d'afegir objectes BeanDefinition al context de treball.
El mètode decorate() permet modificar els objectes BeanDefinition existents.
Un exemple il?lustratiu de la "decoració" és el següent:

<bean id="scopedList" class="java.util.ArrayList" scope="request">
    <aop:scoped-proxy/>
</bean>

L'etiqueta <aop:scoped-proxy> modifica l'element regular <bean> que el conté; proporcionant accés a l'objecte BeanDefinition, aquest el pot modificar.

Per implementar la interfície NamespaceHandler el que farem serà crear una classe que extengui de la classe NamespaceHandlerSupport, la qual ens facilita el procés introduïnt el concepte de "delegar" (mitjançant el registre de totes les instàncies de la classe BeanDefinitionParser que ens calguin) i després implementar la classe BeanDefinitionParser necessaria.

Exemple de classe que hereda de NamespaceHandlerSupport

package net.gencat.ctti.canigo.samples.prototip.spring.xmlExtension;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

public class MyNamespaceHandler extends NamespaceHandlerSupport {

    public void init() {
        registerBeanDefinitionParser("dateformat", new SimpleDateFormatBeanDefinitionParser());
    }
}

Exemple d'implementació de BeanDefinitionParser

package net.gencat.ctti.canigo.samples.prototip.spring.xmlExtension;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;

import java.text.SimpleDateFormat;

public class SimpleDateFormatBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

   protected Class getBeanClass(Element element) {
      return SimpleDateFormat.class;
   }

   protected void doParse(Element element, BeanDefinitionBuilder bean) {
      // this will never be null since the schema explicitly requires that a value be supplied
      String pattern = element.getAttribute("pattern");
      bean.addConstructorArg(pattern);

      // this however is an optional property
      String lenient = element.getAttribute("lenient");
      if (StringUtils.hasText(lenient)) {
         bean.addPropertyValue("lenient", Boolean.valueOf(lenient));
      }
   }
}

3. Crear els fitxers de configuració spring.handlers per a afegir la implementació de la interfície NamespaceHandler a l'estructura de spring; i spring.schemas per a afegir els schemes a l'estructura de spring.

Exemple de fitxer spring.handlers amb els namespaces estàndars

http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler
 http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
 http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
 http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler
 http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
 http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler

Els arxius de configuració de namespaces s'han d'instal?lar a la carpeta META-INF (en l'exemple s'ha instal?lat a una carpeta "resources/META-INF"). Spring ja s'encarrega de fusionar-los.

Exemple de fitxer spring.handlers

http\://www.mycompany.com/schema/myns=net.gencat.ctti.canigo.samples.prototip.spring.xmlExtension.MyNamespaceHandler

Exemple de fitxer spring.schemas

http\://www.mycompany.com/schema/myns/myns.xsd=spring/xmlExtension/myns.xsd

Per utilitzar aquestes definicions es fa de la mateixa manera que amb els namespaces predefinits de spring:
Exemple d'utilització del namespace pròpi

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:myns="http://www.mycompany.com/schema/myns"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.mycompany.com/schema/myns
http://www.mycompany.com/schema/myns/myns.xsd">

   <!-- as a top-level bean -->
   <myns:dateformat id="defaultDateFormat" pattern="yyyy-MM-dd HH:mm" lenient="true"/>
</beans>

Finalment esmentar que per simplificar la generació de metadades BeanDefinition, Spring 2.0 introdueix una nova classe anomenada BeanDefinitionBuilder que ofereix un constructor amb una API que usa el patró creador.

Per a exemples concrets i més informació consulteu la documentació de spring i i concretament l'apèndix B